import re
from typing import List
import time
import os
import psutil  # 需要使用pip install psutil来安装

import re
from typing import List

class Document:
    def __init__(self, file_path: str):
        """
        初始化Document对象,读取文件内容并预处理
        :param file_path: 文件路径
        """
        with open(file_path, 'r', encoding='utf-8') as file:
            self.text = file.read()
            # print(self.text)  # 打印读取的文档
        self.tokens = self.preprocess(self.text)

    @staticmethod
    def preprocess(text: str) -> List[str]:
        """
        预处理文本,移除标点符号、转为小写并分词
        :param text: 原始文本
        :return: 分词后的词元列表
        """
        # text = re.sub(r'[^a-zA-Z0-9\s]', '', text.lower())
        # 修改正则式表达式以包含中文字符
        text = re.sub(r'[^\w\s]', '', text.lower())
        # 分割中文文本时通常不使用空格分割，可以使用jieba等分词工具进行更准确的分词
        # tokens = text.split()
        tokens = re.findall(r'\b\w+\b', text)
        return tokens

class SimilarityDetector:
    @staticmethod
    def compute_similarity(doc1: Document, doc2: Document) -> float:
        """
        计算两篇文档的相似度
        :param doc1: 文档1
        :param doc2: 文档2
        :return: 相似度分数,范围0-1
        """
        tokens1, tokens2 = doc1.tokens, doc2.tokens
        # print(f"Tokens1: {tokens1}")  # 调试语句
        # print(f"Tokens2: {tokens2}")  # 调试语句
        if not tokens1 or not tokens2:
            raise ValueError("输入文档不能为空")

        lcs_len = SimilarityDetector.lcs_length(tokens1, tokens2)
        longer_len = max(len(tokens1), len(tokens2))
        similarity = lcs_len / longer_len
        return round(similarity, 2)

    @staticmethod
    def lcs_length(seq1: List[str], seq2: List[str]) -> int:
        """
        使用动态规划算法计算两个序列的最长公共子序列长度
        :param seq1: 序列1
        :param seq2: 序列2
        :return: 最长公共子序列长度
        """
        m, n = len(seq1), len(seq2)
        memo = [[0] * (n + 1) for _ in range(m + 1)]

        for i in range(1, m + 1):
            for j in range(1, n + 1):
                if seq1[i - 1] == seq2[j - 1]:
                    memo[i][j] = memo[i - 1][j - 1] + 1
                else:
                    memo[i][j] = max(memo[i - 1][j], memo[i][j - 1])

        return memo[m][n]

    @staticmethod
    def get_lcs(seq1: List[str], seq2: List[str]) -> List[str]:
        """
        获取两个序列的最长公共子序列
        :param seq1: 序列1
        :param seq2: 序列2
        :return: 最长公共子序列
        """
        m, n = len(seq1), len(seq2)
        memo = [[0] * (n + 1) for _ in range(m + 1)]

        for i in range(1, m + 1):
            for j in range(1, n + 1):
                if seq1[i - 1] == seq2[j - 1]:
                    memo[i][j] = memo[i - 1][j - 1] + 1
                else:
                    memo[i][j] = max(memo[i - 1][j], memo[i][j - 1])

        # Reconstruct LCS
        lcs = []
        while m > 0 and n > 0:
            if seq1[m - 1] == seq2[n - 1]:
                lcs.append(seq1[m - 1])
                m -= 1
                n -= 1
            elif memo[m - 1][n] >= memo[m][n - 1]:
                m -= 1
            else:
                n -= 1

        return lcs[::-1]  # Return reversed LCS



class PlagiarismChecker:
    @staticmethod
    def highlight_similarity(orig_doc: Document, plagiarized_doc: Document, lcs: List[str]) -> str:
        """
        高亮显示文档中的相似部分
        :param orig_doc: 原始文档对象
        :param plagiarized_doc: 抄袭文档对象
        :param lcs: 最长公共子序列
        :return: 带有高亮相似部分的文档字符串
        """
        highlighted = orig_doc.text
        for token in lcs:
            highlighted = re.sub(f"({token})", r"**\1**", highlighted, count=1)  # 使用正则确保只替换一次
        return highlighted

    @staticmethod
    def count_characters(file_path: str) -> int:
        """
        统计文本文件中的字符数
        :param file_path: 文本文件的路径
        :return: 文件中的字符数
        """
        with open(file_path, 'r', encoding='utf-8') as file:
            content = file.read()
            character_count = len(content)
        return character_count


    @staticmethod
    def run(orig_file: str, plagiarized_file: str, output_file: str):
        """
        运行论文查重程序
        :param orig_file: 原文文件路径
        :param plagiarized_file: 抄袭版文件路径
        :param output_file: 输出文件路径
        """
        try:
            #性能计算代码
            start_time = time.time()  # 开始时间
            process = psutil.Process(os.getpid())  # 获取当前进程
            initial_memory = process.memory_info().rss  # 初始内存使用量

            #查重代码
            orig_characters = PlagiarismChecker.count_characters(orig_file)
            plagiarized_characters = PlagiarismChecker.count_characters(plagiarized_file)

            orig_doc = Document(orig_file)
            plagiarized_doc = Document(plagiarized_file)
            similarity = SimilarityDetector.compute_similarity(orig_doc, plagiarized_doc)
            lcs = SimilarityDetector.get_lcs(orig_doc.tokens, plagiarized_doc.tokens)
            highlighted_text = PlagiarismChecker.highlight_similarity(orig_doc, plagiarized_doc, lcs)

            with open(output_file, 'w', encoding='utf-8') as file:
                file.write(f"查重结果: 文档相似度为 {similarity * 100:.2f}%\n\n")
                file.write(highlighted_text)

            # 打印查重结果和标记过的原文
            print(f"查重结果: 文档相似度为 {similarity * 100:.2f}%")


            end_time = time.time()  # 结束时间
            final_memory = process.memory_info().rss  # 最终内存使用量
            memory_used = final_memory - initial_memory  # 进程使用的内存量

            # 打印性能指标
            print(f"运行时间: {end_time - start_time:.2f} 秒")
            print(f"内存使用: {memory_used / (1024 ** 2):.2f} MB")
            print(f"原文文件字符数: {orig_characters}")
            print(f"抄袭版文件字符数: {plagiarized_characters}")

            # print(f"标记过的原文和对比结果:\n{highlighted_text}")


        except FileNotFoundError:
            print(f"错误: 一个或多个输入文件不存在。")
        except PermissionError:
            print(f"错误: 读取输入文件时没有权限。")
        except ValueError as e:
            print(f"错误: {e}")

if __name__ == "__main__":
    import sys
    if len(sys.argv) != 4:
        print("使用方法: python plagiarism_checker.py <orig_file> <plagiarized_file> <output_file>")
    else:
        PlagiarismChecker.run(sys.argv[1], sys.argv[2], sys.argv[3])

